#include <cstring>
#include <fcntl.h>
#include <memory>
#include <stdio.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <vector>

template <std::size_t BLOCK_SIZE>
struct file_block
{
    uint32_t data_code;
    uint32_t data_len;
    char     data_buf[BLOCK_SIZE] = {0};
};

template <typename BLOCK_DATA>
class block_file
{
  public:
    explicit block_file(const std::string& file_name) : file_name_(file_name), DATA_SIZE(sizeof(BLOCK_DATA))
    {
    }
    ~block_file() = default;
    int load_file(std::vector<std::shared_ptr<BLOCK_DATA>>& result)
    {
        int fd = ::open(file_name_.data(), O_RDONLY);
        if (fd == -1) {
            return -1;
        }
        struct stat st;
        if (fstat(fd, &st) != 0) {
            ::close(fd);
            return -2;
        }
        if (st.st_size % DATA_SIZE != 0) {
            ::close(fd);
            return -3;
        }
        void* file_shm = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
        if (MAP_FAILED == file_shm) {
            ::close(fd);
            return -4;
        }
        std::size_t blk_count = st.st_size / DATA_SIZE;
        result.reserve(blk_count);
        BLOCK_DATA* head_ptr = (BLOCK_DATA*)file_shm;
        for (std::size_t i = 0; i < blk_count; ++i) {
            auto data_ptr = std::make_shared<BLOCK_DATA>();
            std::memcpy(data_ptr.get(), head_ptr, DATA_SIZE);
            result.push_back(data_ptr);
            ++head_ptr;
        }
        munmap(file_shm, st.st_size);
        ::close(fd);
        return 0;
    }
    int append_file(const std::vector<BLOCK_DATA>& data_vec)
    {
        if (data_vec.empty()) {
            return 0;
        }
        FILE* fp = fopen(file_name_.data(), "a");
        if (nullptr == fp) {
            return -1;
        }
        std::size_t n = fwrite((const void*)&*data_vec.begin(), DATA_SIZE, data_vec.size(), fp);
        if (n < data_vec.size()) {
            fclose(fp);
            return -2;
        }
        fclose(fp);
        return 0;
    }

    int append_file(const BLOCK_DATA& data)
    {
        std::vector<BLOCK_DATA> tmp;
        tmp.push_back(data);
        return append_file(tmp);
    }

  private:
    const std::string file_name_;
    const std::size_t DATA_SIZE;
};